package org.roo.addon.compareto;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.ImportMetadata;
import org.springframework.roo.classpath.details.ItdTypeDetailsBuilder;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.ImportRegistrationResolverImpl;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.support.util.CollectionUtils;

/**
 * This type produces metadata for a new ITD. It uses an {@link ItdTypeDetailsBuilder} provided by 
 * {@link AbstractItdTypeDetailsProvidingMetadataItem} to register a field in the ITD and a new method.
 * 
 * @since 1.1.0
 */
public class ComparetoMetadata extends AbstractItdTypeDetailsProvidingMetadataItem {

    // Constants
    private static final String PROVIDES_TYPE_STRING = ComparetoMetadata.class.getName();
    private static final String PROVIDES_TYPE = MetadataIdentificationUtils.create(PROVIDES_TYPE_STRING);
    
    private List<FieldMetadata> compareToFields; 

    public static final String getMetadataIdentiferType() {
        return PROVIDES_TYPE;
    }
    
    public static final String createIdentifier(JavaType javaType, LogicalPath path) {
        return PhysicalTypeIdentifierNamingUtils.createIdentifier(PROVIDES_TYPE_STRING, javaType, path);
    }

    public static final JavaType getJavaType(String metadataIdentificationString) {
        return PhysicalTypeIdentifierNamingUtils.getJavaType(PROVIDES_TYPE_STRING, metadataIdentificationString);
    }

    public static final LogicalPath getPath(String metadataIdentificationString) {
        return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING, metadataIdentificationString);
    }

    public static boolean isValid(String metadataIdentificationString) {
        return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING, metadataIdentificationString);
    }
    
    public ComparetoMetadata(String identifier, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata,
    		List<FieldMetadata> compareToFields) {

    	super(identifier, aspectName, governorPhysicalTypeMetadata);
        
        Validate.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid");
        
        this.compareToFields = compareToFields;
        if(!CollectionUtils.isEmpty(compareToFields)){
        	JavaType comparableInterface = new JavaType("java.lang.Comparable");
        	
        	Collection<ImportMetadata> imports = new ArrayList<ImportMetadata>();
        	ImportMetadata imp = (ImportMetadata) new ImportRegistrationResolverImpl(new 
        			JavaPackage("org.apache.commons.lang3.builder.CompareToBuilder"));
        	imports.add(imp);
        	
        	builder.addImports(imports);
        	builder.addImplementsType(comparableInterface);
        	builder.addMethod(getCompareToMethod());
        }
        
        itdTypeDetails = builder.build();
    }
    
    private MethodMetadata getCompareToMethod(){
    	final JavaType parameterType = JavaType.OBJECT;
    	String parameterName = "obj";
    	final List<JavaSymbolName> parameterNames = Arrays.asList(new JavaSymbolName(parameterName));
    	final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
    	final String typeName = destination.getSimpleTypeName();
    	
    	bodyBuilder.appendFormalLine("System.out.println(\"hello paco\");");
    	bodyBuilder.appendFormalLine("if(!(" + parameterName + " instanceof " + typeName + ")){");
    	bodyBuilder.indent();
    	bodyBuilder.appendFormalLine("return -1;");
    	bodyBuilder.indentRemove();
    	bodyBuilder.appendFormalLine("}");
    	bodyBuilder.appendFormalLine(typeName + " rhs = (" + typeName + ") " + parameterName + ";");
    	
    	final StringBuilder builder = new StringBuilder();
    	builder.append("return new CompareToBuilder()");
    	
    	for(final FieldMetadata field : compareToFields){
    		builder.append(".append(" + field.getFieldName() + ", rhs." + field.getFieldName() + ")");
    	}
    	builder.append(".toComparison();");
    	
    	bodyBuilder.appendFormalLine(builder.toString());
    	
    	final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, 
    			new JavaSymbolName("compareTo"), JavaType.INT_PRIMITIVE,
    			AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder);
    	return methodBuilder.build();
    }
    
    public String toString() {
        final ToStringBuilder builder = new ToStringBuilder(this);
        builder.append("identifier", getId());
        builder.append("valid", valid);
        builder.append("aspectName", aspectName);
        builder.append("destinationType", destination);
        builder.append("governor", governorPhysicalTypeMetadata.getId());
        builder.append("itdTypeDetails", itdTypeDetails);
        return builder.toString();
    }
}
